1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.hiprenderer.backend.d3d.d3dshader;
12 
13 version(Windows):
14 version(DirectX):
15 import hip.config.opts;
16 import hip.hiprenderer.renderer;
17 import hip.api.renderer.texture;
18 import hip.hiprenderer.shader;
19 import hip.hiprenderer.backend.d3d.d3drenderer;
20 import hip.util.system:getWindowsErrorMessage;
21 import directx.d3d11;
22 import directx.d3dcompiler;
23 import directx.d3d11shader;
24 import hip.util.conv:to;
25 import hip.error.handler;
26 
27 
28 class Hip_D3D11_FragmentShader : FragmentShader
29 {
30     ID3DBlob shader;
31     ID3D11PixelShader fs;
32     override final string getDefaultFragment()
33     {
34         return q{
35         float4 main() : SV_TARGET
36         {
37             return float4(1.0f, 1.0f, 1.0f, 1.0f);
38         }};
39     }
40     override final string getFrameBufferFragment()
41     {
42         return q{
43             Texture2D uTex1;
44             SamplerState state;
45 
46             float4 main(float2 inTexST : inTexST) : SV_TARGET
47             {
48                 return uTex1.Sample(state, inTexST);
49             }
50         };
51     }
52     override final string getGeometryBatchFragment()
53     {
54         return q{
55 
56             cbuffer FragVars
57             {
58                 float4 uGlobalColor : uGlobalColor;
59             };
60 
61             float4 main(float4 inVertexColor : inVertexColor) : SV_TARGET
62             {
63                 return inVertexColor * uGlobalColor;
64             }
65         };
66     }
67     /**
68     *   Creates a massive switch case for supporting array of textures.
69     *   D3D11 causes an error if trying to access texture with a variable
70     *   instead of a literal.
71     */
72     override final string getSpriteBatchFragment()
73     {
74         int sup = HipRenderer.getMaxSupportedShaderTextures();
75         string textureSlotSwitchCase = "\tswitch(tid)\n\t{\n"; //Switch textureID
76         for(int i = 1; i < sup; i++)
77         {
78             textureSlotSwitchCase~= "\t\tcase "~ to!string(i)~": "~
79             "return uTex["~to!string(i)~"].Sample(state["~to!string(i)~"], texST) * inVertexColor * uBatchColor;\n";
80         }
81         textureSlotSwitchCase~= "\t\tdefault: return uTex[0].Sample(state[0], texST) * inVertexColor * uBatchColor;";
82         textureSlotSwitchCase~= "\n\t}";
83 
84         return "Texture2D uTex["~to!string(sup)~"];
85 SamplerState state["~to!string(sup)~"];"~q{
86 cbuffer input
87 {
88     float4 uBatchColor: uBatchColor;
89 };
90 
91 float4 main(float4 inVertexColor : inColor, float2 texST : inTexST, float inTexID : inTexID) : SV_TARGET
92 }~"{"~
93 q{
94         // return uBatchColor * uTex.Sample(state, inTexST);
95         int tid = int(inTexID);
96 
97         //switch(tid)...
98         //case 1:
99             //return uTex[1].Sample(state[1], texST) * inVertexColor * uBatchColor;
100 } ~ textureSlotSwitchCase ~ "\n}";
101     }
102     override final string getBitmapTextFragment()
103     {
104         return q{
105 
106             cbuffer FragVars
107             {
108                 float4 uColor : uColor;
109             };
110 
111             Texture2D uSampler1;
112             SamplerState state;
113 
114             float4 main(float2 inTexST : inTexST) : SV_TARGET
115             {
116                 //The texture is read as monochromatic
117                 float r = uSampler1.Sample(state, inTexST)[0];
118 
119                 return float4(r,r,r,r) * uColor;
120             }
121         };
122     }
123 }
124 class Hip_D3D11_VertexShader : VertexShader
125 {
126     ID3DBlob shader;
127     ID3D11VertexShader vs;
128 
129     override final string getDefaultVertex()
130     {
131         return q{
132         float4 main(float2 pos : Position) : SV_POSITION
133         {
134             return float4(pos.x, pos.y, 0.0f, 1.0f);
135         }};
136     }
137     override final string getFrameBufferVertex()
138     {
139         return q{
140             struct VSOut
141             {
142                 float2 inTexST : inTexST;
143                 float4 outPosition : SV_POSITION;
144             };
145 
146             VSOut main(float2 pos : vPosition, float2 vTexST : vTexST)
147             {
148                 VSOut ret;
149                 ret.outPosition = float4(pos.x, pos.y, 0.0, 1.0);
150                 ret.inTexST = vTexST;
151                 return ret;
152             }
153         };
154     }
155     override final string getGeometryBatchVertex()
156     {
157         return q{
158 
159             cbuffer Geom
160             {
161                 float4x4 uModel: uModel;
162                 float4x4 uView : uView;
163                 float4x4 uProj : uProj;
164             };
165             struct VSOut
166             {
167                 float4 inVertexColor : inVertexColor;
168                 float4 outPosition : SV_POSITION;
169             };
170 
171             VSOut main(float3 vPosition: vPosition, float4 vColor: vColor)
172             {
173                 VSOut ret;
174                 ret.outPosition = mul(float4(vPosition, 1.0), mul(mul(uModel, uView), uProj));
175                 ret.inVertexColor = vColor;
176                 return ret;
177             }
178         };
179     }
180     override final string getSpriteBatchVertex()
181     {
182         return q{
183             struct VSOut
184             {
185                 float4 inColor : inColor;
186                 float2 inTexST : inTexST;
187                 float  inTexID : inTexID;
188                 float4 vPosition: SV_POSITION;
189             };
190 
191             cbuffer Cbuf
192             {
193                 float4x4 uProj;
194                 float4x4 uModel;
195                 float4x4 uView;
196             };
197 
198             VSOut main(
199                 float3 pos   : vPosition,
200                 float4 col   : vColor,
201                 float2 texST : vTexST,
202                 float  texID : vTexID
203                 )
204             {
205                 VSOut output;
206                 float4 position = float4(pos.x, pos.y, pos.z, 1.0f);
207                 output.vPosition = mul(position, mul(mul(uModel, uView), uProj));
208 
209                 output.inTexST = texST;
210                 output.inColor = col;
211                 output.inTexID = texID;
212                 return output;
213             }
214         };
215     }
216     override final string getBitmapTextVertex()
217     {
218         return q{
219 
220             cbuffer Cbuf
221             {
222                 float4x4 uModel;
223                 float4x4 uView;
224                 float4x4 uProj;
225             };
226 
227             struct VSOut
228             {
229                 float2 inTexST : inTexST;
230                 float4 outPosition : SV_POSITION;
231             };
232 
233             VSOut main(float2 vPosition : vPosition, float2 vTexST : vTexST)
234             {
235                 VSOut ret;
236                 ret.outPosition = mul(float4(vPosition, 1.0, 1.0), mul(mul(uModel, uView), uProj));
237                 ret.inTexST = vTexST;
238                 return ret;
239             }
240         };
241     }
242 }
243 class Hip_D3D11_ShaderProgram : ShaderProgram
244 {
245     Hip_D3D11_VertexShader vs;
246     Hip_D3D11_FragmentShader fs;
247 
248     protected HipBlendFunction blendSrc, blendDst;
249     protected HipBlendEquation blendEq;
250     protected ID3D11BlendState blendState;
251 
252     ID3D11ShaderReflection vReflector;
253     ID3D11ShaderReflection pReflector;
254 
255     bool initialize()
256     {
257         import hip.hiprenderer;
258         auto hres = D3DReflect(vs.shader.GetBufferPointer(),
259         vs.shader.GetBufferSize(), &IID_ID3D11ShaderReflection, cast(void**)&vReflector);
260         if(FAILED(hres))
261         {
262             ErrorHandler.showErrorMessage("D3D11 ShaderProgram initialization", 
263             "Could not get the reflection interface from the vertex shader, error: "~ getWindowsErrorMessage(hres));
264             return false;
265         }
266         hres = D3DReflect(fs.shader.GetBufferPointer(),
267         fs.shader.GetBufferSize(), &IID_ID3D11ShaderReflection, cast(void**)&pReflector);
268         if(FAILED(hres))
269         {
270             ErrorHandler.showErrorMessage("D3D11 ShaderProgram initialization", 
271             "Could not get the reflection interface from the pixel shader, error: "~ getWindowsErrorMessage(hres));
272             return false;
273         }
274         return true;
275     }
276 }
277 
278 struct Hip_D3D11_ShaderVarAdditionalData
279 {
280     ID3D11Buffer buffer;
281     uint id;
282 }
283 
284 package D3D11_BLEND getD3DBlendFunc(HipBlendFunction func)
285 {
286     final switch(func) with(HipBlendFunction)
287     {
288         case  ZERO: return D3D11_BLEND_ZERO;
289         case  ONE: return D3D11_BLEND_ONE;
290 
291         case  SRC_COLOR: return D3D11_BLEND_SRC_COLOR;
292         case  ONE_MINUS_SRC_COLOR: return D3D11_BLEND_INV_SRC_COLOR;
293     
294         case  DST_COLOR: return D3D11_BLEND_DEST_COLOR;
295         case  ONE_MINUS_DST_COLOR: return D3D11_BLEND_INV_DEST_COLOR;
296         
297         case  SRC_ALPHA: return D3D11_BLEND_SRC_ALPHA;
298         case  ONE_MINUS_SRC_ALPHA: return  D3D11_BLEND_INV_SRC_ALPHA;
299     
300         case  DST_ALPHA: return  D3D11_BLEND_DEST_ALPHA;
301         case  ONE_MINUS_DST_ALPHA: return D3D11_BLEND_INV_DEST_ALPHA;
302     
303         case  CONSTANT_COLOR: return D3D11_BLEND_SRC1_COLOR;
304         case  ONE_MINUS_CONSTANT_COLOR: return D3D11_BLEND_INV_SRC1_COLOR;
305         
306         case  CONSTANT_ALPHA: return D3D11_BLEND_SRC1_ALPHA;
307         case  ONE_MINUS_CONSTANT_ALPHA: return D3D11_BLEND_INV_SRC1_ALPHA;
308     }
309 }
310 package D3D11_BLEND_OP getD3DBlendEquation(HipBlendEquation eq)
311 {
312     final switch(eq) with (HipBlendEquation)
313     {
314         case DISABLED:return D3D11_BLEND_OP_ADD;
315         case ADD:return D3D11_BLEND_OP_ADD;
316         case SUBTRACT:return D3D11_BLEND_OP_SUBTRACT;
317         case REVERSE_SUBTRACT:return D3D11_BLEND_OP_REV_SUBTRACT;
318         case MIN:return D3D11_BLEND_OP_MIN;
319         case MAX:return D3D11_BLEND_OP_MAX;
320     }
321 }
322 
323 class Hip_D3D11_ShaderImpl : IShader
324 {
325     import hip.util.data_structures:Pair;
326     FragmentShader createFragmentShader()
327     {
328         Hip_D3D11_FragmentShader fs = new Hip_D3D11_FragmentShader();
329         fs.shader = null;
330         fs.fs = null;
331         return cast(FragmentShader)fs;
332     }
333     VertexShader createVertexShader()
334     {
335         Hip_D3D11_VertexShader vs = new Hip_D3D11_VertexShader();
336         vs.shader = null;
337         vs.vs = null;
338         return cast(VertexShader)vs;
339     }
340     ShaderProgram createShaderProgram()
341     {
342         Hip_D3D11_ShaderProgram prog = new Hip_D3D11_ShaderProgram();
343         return prog;
344     }
345 
346     bool compileShader(ref ID3DBlob shaderPtr, string shaderPrefix, string shaderSource)
347     {
348         shaderSource~="\0";
349 
350         string shaderType = shaderPrefix == "ps" ? "Pixel Shader" : "Vertex Shader";
351         char* source = cast(char*)shaderSource.ptr; 
352 
353         //No #includes
354 
355         uint compile_flags = D3DCOMPILE_ENABLE_STRICTNESS;
356         uint effects_flags = 0;
357         ID3DBlob shader = null;
358         ID3DBlob error = null;
359         shaderPrefix~= "_5_0\0"; //Append version on shader type
360 
361 
362         static if(HIP_DEBUG)
363         {
364             compile_flags|= D3DCOMPILE_WARNINGS_ARE_ERRORS;
365             compile_flags|= D3DCOMPILE_DEBUG;
366             compile_flags|= D3DCOMPILE_SKIP_OPTIMIZATION;
367         }
368         else static if(HIP_OPTIMIZE)
369             compile_flags|= D3DCOMPILE_OPTIMIZATION_LEVEL3;
370 
371         const D3D_SHADER_MACRO[] defines = 
372         [
373             cast(D3D_SHADER_MACRO)null, cast(D3D_SHADER_MACRO)null
374         ];
375 
376         HRESULT hr = D3DCompile(source, shaderSource.length+1, null,
377         defines.ptr, null, "main",  shaderPrefix.ptr, compile_flags, effects_flags, &shader, &error);
378         shaderPtr = shader;
379 
380         if(ErrorHandler.assertLazyErrorMessage(SUCCEEDED(hr), shaderType~" compilation error", "Compilation failed"))
381         {
382             if(error !is null)
383             {
384                 string errMessage = to!string(cast(char*)error.GetBufferPointer());
385                 ErrorHandler.showErrorMessage("Shader Source Error: ", shaderSource);
386                 ErrorHandler.showErrorMessage("Compilation error:", errMessage);
387                 error.Release();
388             }
389             if(shader)
390                 shader.Release();
391             return false;
392         }
393         return true;
394     }
395     bool compileShader(VertexShader _vs, string shaderSource)
396     {
397         Hip_D3D11_VertexShader vs = cast(Hip_D3D11_VertexShader)_vs;
398         bool compiledCorrectly = compileShader(vs.shader, "vs", shaderSource);
399         if(compiledCorrectly)
400         {
401             auto res = _hip_d3d_device.CreateVertexShader(vs.shader.GetBufferPointer(),
402             vs.shader.GetBufferSize(), null, &vs.vs);
403             if(ErrorHandler.assertErrorMessage(SUCCEEDED(res), "Vertex shader creation error", "Creation failed"))
404             {
405                 ErrorHandler.showErrorMessage("Vertex Shader Error:", getWindowsErrorMessage(res));
406                 compiledCorrectly = false;
407             }
408         }
409         return compiledCorrectly;
410     }
411     bool compileShader(FragmentShader _fs, string shaderSource)
412     {
413         auto fs = cast(Hip_D3D11_FragmentShader)_fs;
414         bool compiledCorrectly = compileShader(fs.shader, "ps", shaderSource);
415         if(compiledCorrectly)
416         {
417             auto res = _hip_d3d_device.CreatePixelShader(fs.shader.GetBufferPointer(), fs.shader.GetBufferSize(), null, &fs.fs);
418             if(ErrorHandler.assertErrorMessage(SUCCEEDED(res), "Fragment/Pixel shader creation error", "Creation failed"))
419             {
420                 ErrorHandler.showErrorMessage("Fragment Shader Error:", getWindowsErrorMessage(res));
421                 compiledCorrectly = false;
422             }
423         }
424         return compiledCorrectly;
425     }
426 
427     bool linkProgram(ref ShaderProgram _program, VertexShader vs,  FragmentShader fs)
428     {
429         auto program = cast(Hip_D3D11_ShaderProgram)_program;
430         program.vs = cast(Hip_D3D11_VertexShader)vs;
431         program.fs = cast(Hip_D3D11_FragmentShader)fs;
432         return program.initialize();
433     }
434 
435 
436     /**
437     *   params:
438     *       layoutIndex: The layout index defined on shader
439     *       valueAmount: How many values using, for 3 vertices, you can use 3
440     *       dataType: Which data type to send
441     *       normalize: If it will normalize
442     *       stride: Target value amount in bytes, for instance, vec3 is float.sizeof*3
443     *       offset: It will be calculated for each value index
444     *       
445     */
446     void sendVertexAttribute(uint layoutIndex, int valueAmount, uint dataType, bool normalize, uint stride, int offset)
447     {
448         // glVertexAttribPointer(layoutIndex, valueAmount, dataType, normalize, stride, cast(void*)offset);
449         // glEnableVertexAttribArray(layoutIndex);
450     }
451 
452     void bind(ShaderProgram _program)
453     {
454         Hip_D3D11_ShaderProgram p = cast(Hip_D3D11_ShaderProgram)_program;
455         if(p.blendState !is null && 
456             (p.blendDst != currDst ||
457             p.blendSrc != currSrc ||
458             p.blendEq != currEq))
459         {
460             currEq  = p.blendEq;
461             currSrc = p.blendSrc;
462             currDst = p.blendDst;
463             _hip_d3d_context.OMSetBlendState(p.blendState, null, 0xFF_FF_FF_FF);
464         }
465         _hip_d3d_context.VSSetShader(p.vs.vs, cast(ID3D11ClassInstance*)0, 0u);
466         _hip_d3d_context.PSSetShader(p.fs.fs, cast(ID3D11ClassInstance*)0, 0u);
467     }
468     void unbind(ShaderProgram _program)
469     {
470         _hip_d3d_context.VSSetShader(null, cast(ID3D11ClassInstance*)0, 0u);
471         _hip_d3d_context.PSSetShader(null, cast(ID3D11ClassInstance*)0, 0u);
472     }
473 
474     int getId(ref ShaderProgram prog, string name)
475     {
476         import hip.error.handler;
477         Hip_D3D11_ShaderProgram p = cast(Hip_D3D11_ShaderProgram)prog;
478         D3D11_SHADER_INPUT_BIND_DESC output;
479 
480         
481         if(!SUCCEEDED(p.vReflector.GetResourceBindingDescByName(name.ptr, &output)))
482         {
483             ErrorHandler.showErrorMessage("Error finding ID/Uniform for shader ", "For variable named "~name ~ " in shader " ~ prog.name);
484         }
485 
486         
487         return output.BindPoint;
488     }
489 
490     void sendVars(ref ShaderProgram prog, ShaderVariablesLayout[string] layouts)
491     {
492         D3D11_SHADER_INPUT_BIND_DESC desc;
493         Hip_D3D11_ShaderProgram p = cast(Hip_D3D11_ShaderProgram)prog;
494         foreach(k, _; layouts)
495         {
496             import core.stdc.string:memcpy;
497             ShaderVariablesLayout l = cast(ShaderVariablesLayout)layouts[k];
498             Hip_D3D11_ShaderVarAdditionalData* data = cast(Hip_D3D11_ShaderVarAdditionalData*)l.getAdditionalData();
499             D3D11_MAPPED_SUBRESOURCE resource;
500             _hip_d3d_context.Map(data.buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
501             memcpy(resource.pData, l.getBlockData(), l.getLayoutSize());
502             _hip_d3d_context.Unmap(data.buffer,  0);
503             
504             ErrorHandler.assertExit(data != null, "D3D11 ShaderVarAdditionalData is null, can't send variables");
505             
506             final switch(l.shaderType)
507             {
508                 case ShaderTypes.FRAGMENT:
509                     p.pReflector.GetResourceBindingDescByName((l.name~"\0").ptr, &desc);
510                     _hip_d3d_context.PSSetConstantBuffers(desc.BindPoint, 1, &data.buffer);
511                     break;
512                 case ShaderTypes.VERTEX:
513                     p.vReflector.GetResourceBindingDescByName((l.name~"\0").ptr, &desc);
514                     _hip_d3d_context.VSSetConstantBuffers(desc.BindPoint, 1, &data.buffer);
515                     break;
516                 case ShaderTypes.GEOMETRY:
517                 case ShaderTypes.NONE:
518                     break;
519             }
520         }
521     }
522 
523     void bindArrayOfTextures(ref ShaderProgram prog, IHipTexture[] textures, string varName)
524     {
525         foreach(i, texture; textures)
526             texture.bind(cast(int)i);
527     }
528 
529     void createVariablesBlock(ref ShaderVariablesLayout layout)
530     {
531         import core.stdc.stdlib:malloc;
532         D3D11_BUFFER_DESC desc;
533         desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
534         desc.StructureByteStride=0;
535         desc.MiscFlags = 0;
536         desc.Usage = D3D11_USAGE_DYNAMIC;
537         desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
538         desc.ByteWidth = cast(uint)layout.getLayoutSize();
539         D3D11_SUBRESOURCE_DATA data;
540         data.pSysMem = layout.getBlockData();
541 
542         Hip_D3D11_ShaderVarAdditionalData* d = cast(Hip_D3D11_ShaderVarAdditionalData*)
543             malloc(Hip_D3D11_ShaderVarAdditionalData.sizeof);
544         HRESULT res = _hip_d3d_device.CreateBuffer(&desc, &data, &d.buffer);
545         layout.setAdditionalData(cast(void*)d, true);
546 
547         if(FAILED(res))
548             ErrorHandler.showErrorMessage("D3D11 Error while creating variables block",
549             "Error while creating variable buffer for Shader with type "~to!string(layout.shaderType));
550     }
551 
552     protected __gshared HipBlendFunction currSrc, currDst;
553     protected __gshared HipBlendEquation currEq;
554 
555 
556     void setBlending(ShaderProgram prog, HipBlendFunction src, HipBlendFunction dest, HipBlendEquation eq)
557     {
558         Hip_D3D11_ShaderProgram p = cast(Hip_D3D11_ShaderProgram)prog;
559         p.blendSrc = src;
560         p.blendDst = dest;
561         p.blendEq = eq;
562         auto b = &Hip_D3D11_Renderer.blend.RenderTarget[0];
563 
564         if(eq == HipBlendEquation.DISABLED)
565         {
566             b.BlendEnable = cast(int)false;
567         }
568         else
569         {
570             b.BlendEnable = cast(int)true;
571             b.SrcBlend = getD3DBlendFunc(src);
572             b.DestBlend = getD3DBlendFunc(dest);
573             b.BlendOp = getD3DBlendEquation(eq);
574             b.BlendOpAlpha = getD3DBlendEquation(eq);
575 
576             b.SrcBlendAlpha = getD3DBlendFunc(HipBlendFunction.ZERO);
577             b.DestBlendAlpha = getD3DBlendFunc(HipBlendFunction.ZERO);
578             b.BlendOp = getD3DBlendEquation(HipBlendEquation.ADD);
579             b.BlendOpAlpha = getD3DBlendEquation(HipBlendEquation.ADD);
580             b.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
581         }        
582         _hip_d3d_device.CreateBlendState(&Hip_D3D11_Renderer.blend, &p.blendState);
583     }
584     
585 
586     void deleteShader(FragmentShader* _fs){}
587     void deleteShader(VertexShader* _vs){}
588     void dispose(ref ShaderProgram prog)
589     {
590         Hip_D3D11_ShaderProgram p = cast(Hip_D3D11_ShaderProgram)prog;
591         auto fs = p.fs;
592         if(fs.shader !is null)
593             fs.shader.Release();
594         fs.shader = null;
595         if(fs.fs !is null)
596             fs.fs.Release();
597         fs.fs = null;
598         auto vs = p.vs;
599         if(vs.shader !is null)
600             vs.shader.Release();
601         vs.shader = null;
602         if(vs.vs !is null)
603             vs.vs.Release();
604         vs.vs = null;
605     }
606     
607     bool setShaderVar(ShaderVar* sv, ShaderProgram prog, void* value)
608     {
609         return false;
610     }
611 
612     override void onRenderFrameEnd(ShaderProgram){}
613 
614     
615 }